AC_PREREQ([2.60])
AC_INIT([libwallycore],[0.7.7])
AC_CONFIG_AUX_DIR([tools/build-aux])
AC_CONFIG_MACRO_DIR([tools/build-aux/m4])
AC_CONFIG_SRCDIR([src/mnemonic.h])
AC_CONFIG_HEADERS([src/config.h])
AC_CANONICAL_HOST
AH_TOP([#ifndef LIBWALLYCORE_CONFIG_H
#define LIBWALLYCORE_CONFIG_H])
AH_BOTTOM([#include "ccan_config.h"
#endif /*LIBWALLYCORE_CONFIG_H*/])

# Remove any optimisation/debug flags from CFLAGS, these are controlled
# only by --enable-debug, in order to only use tested configurations
CFLAGS=`echo $CFLAGS | sed 's/-[Og][0-9]*[ $$]*//'`

LDPATH_VAR=LD_LIBRARY_PATH
case $host_os in
*darwin*)
    is_osx="yes"
    LDPATH_VAR=DYLD_LIBRARY_PATH
    ;;
esac
AM_CONDITIONAL([IS_OSX], [test "x$is_osx" == "xyes"])
AC_SUBST([LDPATH_VAR])

AM_INIT_AUTOMAKE([foreign subdir-objects])
LT_INIT([disable-static])

m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])

AC_PROG_CC

# Prefer the compilers native ar/ranlib if available
# We have to manually loop to test these as autoconf inexplicably does
# not provide an AC_PATH_TOOLS macro for this purpose.
candidate_ars="ar"
candidate_ranlibs="ranlib"
# gcc bugs cause memory clearing to fail randomly. clang exhibits much more
# consistent and sane behaviour so should be preferred going forward.
enable_clear_tests="no"
case $CC in
*gcc*)
    candidate_ars="gcc-ar ar"
    candidate_ranlibs="gcc-ranlib ranlib"
    ;;
*clang*)
    ver=$($CC --version | head -n 1 | cut -d' '  -f3 | cut -d'.' -f1)
    candidate_ars="llvm-ar-$ver llvm-ar ar"
    candidate_ranlibs="llvm-ranlib-$ver llvm-ranlib ranlib"
    enable_clear_tests="yes"
    ;;
esac

if test "x$is_osx" == "xyes"; then
    candidate_ars="libtool $candidate_ars"
    CCDIR=`dirname $CC`
    if test x"$CCDIR" != x"."; then
        if test -x $CCDIR/libtool; then
            # Use libtool from the same directory as our clang
            AR=$CCDIR/libtool
            candidate_ars=""
        fi
    fi
fi

if test -n "$candidate_ars"; then
    for candidate in $candidate_ars; do
        AC_PATH_TOOL(AR, $candidate)
        if test "x$HAVE_AR" == "xyes"; then
            break
        fi
    done
fi
case $AR in
*libtool)
    ARFLAGS="-static -o"
    AR_FLAGS="-static -o"
    AC_SUBST([ARFLAGS])
    AC_SUBST([AR_FLAGS])
    ;;
esac

for candidate in $candidate_ranlibs; do
    AC_PATH_TOOL(RANLIB, $candidate)
    if test "x$HAVE_RANLIB" == "xyes"; then
        break
    fi
done

AC_SUBST([AR])
AC_SUBST([RANLIB])

GNU_SED=sed
AC_CHECK_PROG(HAVE_GSED,gsed,yes,no)
if test "x$HAVE_GSED" == "xyes"; then
  GNU_SED=gsed
else
    if test "x$is_osx" == "xyes"; then
        AC_MSG_ERROR([gsed must be available to build this library])
    fi
fi
AC_SUBST([GNU_SED])

#
# C facilities
#
AC_ARG_ENABLE(debug,
    AS_HELP_STRING([--enable-debug],[enable debugging (default: no)]),
    [debug=$enableval], [debug=no])
AC_ARG_ENABLE(coverage,
    AS_HELP_STRING([--enable-coverage],[enable code coverage (default: no)]),
    [coverage=$enableval], [coverage=no])
AC_ARG_ENABLE(tests,
    AS_HELP_STRING([--enable-tests],[enable code tests (default: yes)]),
    [tests=$enableval], [tests=yes])
AC_ARG_ENABLE(elements,
    AS_HELP_STRING([--enable-elements],[enable elements tx code (default: no)]),
    [elements=$enableval], [elements=no])
AC_ARG_ENABLE(builtin-memset,
    AS_HELP_STRING([--enable-builtin-memset],[disable to add -fno-builtin-memset to compiler flags. helps with explicit_bzero/memset being elided on Linux clang 7.0.1 and up (default: yes)]),
    [builtin_memset=$enableval], [builtin_memset=yes])
AM_CONDITIONAL([RUN_TESTS], [test "x$tests" == "xyes"])
AM_CONDITIONAL([BUILD_ELEMENTS], [test "x$elements" == "xyes"])

AC_C_BIGENDIAN()
AC_C_INLINE
AC_TYPE_SIZE_T
AC_TYPE_UINT64_T
AC_TYPE_UINT32_T
AC_TYPE_UINT16_T
AC_TYPE_UINT8_T

AM_CFLAGS=
if test "x$debug" == "xyes"; then
    # Make debugging easier, leave assertions in
    AX_CHECK_COMPILE_FLAG([-O0], [AM_CFLAGS="$AM_CFLAGS -O0"])
    AX_CHECK_COMPILE_FLAG([-ggdb], [AM_CFLAGS="$AM_CFLAGS -ggdb"])
    AX_CHECK_LINK_FLAG([-O0], [LDFLAGS="$LDFLAGS -O0"])
    AX_CHECK_LINK_FLAG([-ggdb], [LDFLAGS="$LDFLAGS -ggdb"])
    if test "x$coverage" == "xyes"; then
        AX_CHECK_COMPILE_FLAG([-fprofile-arcs -ftest-coverage], [AM_CFLAGS="$AM_CFLAGS -fprofile-arcs -ftest-coverage"])
        AX_CHECK_LINK_FLAG([-lgcov], [LDFLAGS="$LDFLAGS -lgcov"])
    fi
else
    # Optimise and harden if we can
    AX_CHECK_COMPILE_FLAG([-O3], [AM_CFLAGS="$AM_CFLAGS -O3"])
    AC_CHECK_DEFINE([_FORTIFY_SOURCE], [], [
      AX_CHECK_COMPILE_FLAG([-D_FORTIFY_SOURCE=2],
        [CPPFLAGS="$CPPFLAGS -D_FORTIFY_SOURCE=2"])
    ])
    AX_CHECK_COMPILE_FLAG([-fstack-protector-strong], [AM_CFLAGS="$AM_CFLAGS -fstack-protector-strong"])
    AX_CHECK_COMPILE_FLAG([-DNDEBUG=1], [AM_CFLAGS="$AM_CFLAGS -DNDEBUG=1"])
    AX_CHECK_LINK_FLAG([-O3], [LDFLAGS="$LDFLAGS -O3"])
    AX_CHECK_LINK_FLAG([-Wl,-z,relro], [LDFLAGS="$LDFLAGS -Wl,-z,relro"])
fi

if test "x$elements" == "xyes"; then
    AX_CHECK_COMPILE_FLAG([-DBUILD_ELEMENTS=1], [AM_CFLAGS="$AM_CFLAGS -DBUILD_ELEMENTS=1"])
fi

if test "x$builtin_memset" == "xno"; then
    AX_CHECK_COMPILE_FLAG([-fno-builtin-memset], [AM_CFLAGS="$AM_CFLAGS -fno-builtin"])
fi

# -flax-vector-conversions is needed for our arm assembly
AX_CHECK_COMPILE_FLAG([-flax-vector-conversions], [AM_CFLAGS="$AM_CFLAGS -flax-vector-conversions"])
AX_CHECK_COMPILE_FLAG([-fno-strict-aliasing], [NOALIAS_CFLAGS="$AM_CFLAGS -fno-strict-aliasing $AM_CFLAGS"])

AX_CHECK_COMPILE_FLAG([-Wformat-nonliteral], [AM_CFLAGS="-Wformat-nonliteral $AM_CFLAGS"])
AX_CHECK_COMPILE_FLAG([-Wformat-security], [AM_CFLAGS="-Wformat-security $AM_CFLAGS"])
AX_CHECK_COMPILE_FLAG([-Wformat], [AM_CFLAGS="-Wformat $AM_CFLAGS"])
AX_CHECK_COMPILE_FLAG([-Wstrict-prototypes], [AM_CFLAGS="-Wstrict-prototypes $AM_CFLAGS"])
AX_CHECK_COMPILE_FLAG([-Wshadow], [AM_CFLAGS="-Wshadow $AM_CFLAGS"])
AX_CHECK_COMPILE_FLAG([-Wnested-externs], [AM_CFLAGS="-Wnested-externs $AM_CFLAGS"])
AX_CHECK_COMPILE_FLAG([-Wcast-align], [AM_CFLAGS="-Wcast-align $AM_CFLAGS"])
AX_CHECK_COMPILE_FLAG([-Wpedantic], [AM_CFLAGS="-Wpedantic $AM_CFLAGS"])
AX_CHECK_COMPILE_FLAG([-Wextra], [AM_CFLAGS="-Wextra $AM_CFLAGS"])
AX_CHECK_COMPILE_FLAG([-Wall], [AM_CFLAGS="-Wall $AM_CFLAGS"])

# Disable some unhelpful errors from those enabled above
AX_CHECK_COMPILE_FLAG([-Wno-unused-function], [AM_CFLAGS="$AM_CFLAGS -Wno-unused-function"])
AX_CHECK_COMPILE_FLAG([-Wno-long-long], [AM_CFLAGS="$AM_CFLAGS -Wno-long-long"])
AX_CHECK_COMPILE_FLAG([-Wno-overlength-strings], [AM_CFLAGS="$AM_CFLAGS -Wno-overlength-strings"])
AX_CHECK_COMPILE_FLAG([-Wno-variadic-macros], [AM_CFLAGS="$AM_CFLAGS -Wno-variadic-macros"])

if [[ "$CC" == *"clang"* ]]; then
    AX_CHECK_COMPILE_FLAG([-Wno-gnu-statement-expression], [AM_CFLAGS="$AM_CFLAGS -Wno-gnu-statement-expression"])
    AX_CHECK_COMPILE_FLAG([-Wno-zero-length-array], [AM_CFLAGS="$AM_CFLAGS -Wno-zero-length-array"])
    AX_CHECK_COMPILE_FLAG([-Wno-language-extension-token], [AM_CFLAGS="$AM_CFLAGS -Wno-language-extension-token"])
    AX_CHECK_COMPILE_FLAG([-Wno-unknown-attributes], [AM_CFLAGS="$AM_CFLAGS -Wno-unknown-attributes"])
fi
AC_SUBST([NOALIAS_CFLAGS])

# Under OSX the compiler accepts this flag but the linker then fails.
if test "x$is_osx" != "xyes"; then
    AX_CHECK_COMPILE_FLAG([-Wl,--whole-archive], [whole_archive=yes])
fi

# SWIG versions vary in generated code quality; skip warnings
SWIG_WARN_CFLAGS="-fno-strict-aliasing"
AX_CHECK_COMPILE_FLAG([-Wno-shadow], [SWIG_WARN_CFLAGS="$SWIG_WARN_CFLAGS -Wno-shadow"])
AX_CHECK_COMPILE_FLAG([-Wno-self-assign], [SWIG_WARN_CFLAGS="$SWIG_WARN_CFLAGS -Wno-self-assign"])
AX_CHECK_COMPILE_FLAG([-Wno-missing-field-initializers], [SWIG_WARN_CFLAGS="$SWIG_WARN_CFLAGS -Wno-missing-field-initializers"])
AC_SUBST([SWIG_WARN_CFLAGS])

AC_ARG_ENABLE(export-all,
    AS_HELP_STRING([--enable-export-all],[export all functions (for testing, default: no)]),
    [export_all=$enableval], [export_all=no])
AM_CONDITIONAL([EXPORT_ALL], [test "x$export_all" == "xyes"])

if test "x$export_all" != "xyes"; then
    AX_CHECK_COMPILE_FLAG([-fvisibility=hidden], [AM_CFLAGS="$AM_CFLAGS -fvisibility=hidden"])
fi

# Assume we have no unaligned access if cross-compiling
AC_RUN_IFELSE([AC_LANG_SOURCE([[int main(void){static int a[2];return *((int*)(((char*)a)+1)) != 0;}]])],
              have_unaligned=1, have_unaligned=0, have_unaligned=0)
AC_DEFINE_UNQUOTED([HAVE_UNALIGNED_ACCESS], [$have_unaligned], [Define if we have unaligned access])

if test "x$is_osx" != "xyes"; then
    # Assume we are using gcc (i.e. have this attribute) if cross-compiling
    AC_COMPILE_IFELSE([AC_LANG_SOURCE([extern int foo(int) __attribute__((weak)); int main(void){return 0;}])],
                      [AC_DEFINE([HAVE_ATTRIBUTE_WEAK], 1, [Define if we have __attribute__((weak))])])
fi

AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <sys/mman.h>]],[[mmap(0,0,0,0,0,0)]])],
               [AC_DEFINE(HAVE_MMAP, 1, [Define if we have mmap])])

AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <stdlib.h>]],[[return posix_memalign(NULL,0,0)]])],
               [AC_DEFINE(HAVE_POSIX_MEMALIGN, 1, [Define if we have posix_memalign])])

AC_CHECK_FUNCS([memset_s explicit_bzero explicit_memset])

AC_MSG_CHECKING(whether we can use inline asm code)
AC_LINK_IFELSE([AC_LANG_PROGRAM([[
]], [[
int a = 42;
int *pnt = &a;
__asm__ __volatile__ ("" : : "r"(pnt) : "memory");
]])],
  [AC_MSG_RESULT(yes)
   AC_DEFINE([HAVE_INLINE_ASM], [1], [inline asm code can be used])],
  [AC_MSG_RESULT(no)]
)

AC_CHECK_HEADERS([byteswap.h, sys/mman.h])

AX_PTHREAD([ac_have_pthread=yes], [ac_have_pthread=no])
AM_CONDITIONAL([USE_PTHREAD], [test "x$ac_have_pthread" == "xyes" -a "x$enable_clear_tests" == "xyes"])
if test "x$ac_have_pthread" == "xyes"; then
    AC_DEFINE([HAVE_PTHREAD], 1, [Define if we have pthread support])
    AC_CHECK_HEADERS([asm/page.h])
fi

#
# libsecp256k1
#
# FIXME: This is needed to force libtool to use all object files from secp.
#        We can only build secp properly by recursively invoking
#        configure/make, and can't include it as a noinst_ library. Libtool
#        assumes that such libraries will be installed along with our library
#        target and so won't force all object files in the library to be
#        included in ours - despite the fact that we are making a shared
#        library and linking to a static one. This is broken and we work
#        around it by hacking the whole-archive flags into the _LDADD variable
#        for wallycore.
#        Because automake tries to police its users very strictly and fails
#        hard when flags are passed in this way, we have to substitute the
#        flags here.
#        Because libtool both intercepts -Wl and arbitrarily re-orders its
#        command line inputs, we have to concoct a single expression to
#        enforce linking that cannot be split, hence the below expression.
LIBADD_SECP256K1="secp256k1/.libs/libsecp256k1.a"
if test "x$whole_archive" == "xyes"; then
    LIBADD_SECP256K1="-Wl,--whole-archive,secp256k1/.libs/libsecp256k1.a,--no-whole-archive"
fi
AC_SUBST([LIBADD_SECP256K1])

#
# Python facilities
#
AX_PYTHON_DEVEL([>= '2.7.0'])
AM_CONDITIONAL([HAVE_PYTHON], [test "$PYTHON" != ""])
AM_CONDITIONAL([RUN_PYTHON_TESTS], [test "$PYTHON" != "" -a "x$pythonexists" == "xyes"])

#
# SWIG
#
AC_PROG_SWIG

AC_ARG_ENABLE(swig-python,
    AS_HELP_STRING([--enable-swig-python],[enable the SWIG python interface (default: no)]),
    [swig_python=$enableval], [swig_python=no])
AM_CONDITIONAL([USE_SWIG_PYTHON], [test "x$swig_python" == "xyes"])

if test "x$swig_python" == "xyes"; then
    if test "x$pythonexists" != "xyes"; then
        AC_MSG_FAILURE([ERROR: No usable python was found for swig-python])
    fi
    SWIG_PYTHON
    AX_CHECK_COMPILE_FLAG([-DSWIG_PYTHON_BUILD=1], [AM_CFLAGS="$AM_CFLAGS -DSWIG_PYTHON_BUILD=1"])
fi

AC_ARG_ENABLE(js-wrappers,
    AS_HELP_STRING([--enable-js-wrappers],[enable the Javascript interface wrappers (default: no)]),
    [js_wrappers=$enableval], [js_wrappers=no])
AM_CONDITIONAL([USE_JS_WRAPPERS], [test "x$js_wrappers" == "xyes"])

if test "x$js_wrappers" == "xyes"; then
    if test "x$pythonexists" != "xyes"; then
        AC_MSG_FAILURE([ERROR: No usable python was found for generating js wrappers])
    fi
    if test "x$swig_python" != "xyes"; then
        SWIG_PYTHON
    fi
    AX_CHECK_COMPILE_FLAG([-DSWIG_JAVASCRIPT_BUILD=1], [AM_CFLAGS="$AM_CFLAGS -DSWIG_JAVASCRIPT_BUILD=1"])
    AC_CHECK_PROG(HAVE_YARN,yarn,yes,no)
    if test "x$HAVE_YARN" != "xyes"; then
        AC_MSG_FAILURE([ERROR: No usable yarn was found, please install yarn before re-running])
    fi
fi

# Set node-gyp flags regardless of  JS enablement; they are used for wrapper
# generation which can be usefully run without enabling JS/requiring yarn
NODE_GYP_DIR="Release"
if test "x$debug" == "xyes"; then
    NODE_GYP_FLAGS="--debug"
    NODE_GYP_DIR="Debug"
fi
AC_SUBST([NODE_GYP_FLAGS])
AC_SUBST([NODE_GYP_DIR])

AC_ARG_ENABLE(swig-java,
    AS_HELP_STRING([--enable-swig-java],[enable the SWIG java (JNI) interface (default: no)]),
    [swig_java=$enableval], [swig_java=no])
AM_CONDITIONAL([USE_SWIG_JAVA], [test "x$swig_java" == "xyes"])

if test "x$swig_java" == "xyes"; then
    saved_JAVA_HOME=$JAVA_HOME
    if test x"$cross_compiling" = "xyes"; then
        # For cross compiling we assume the users host O/S Java install is not
        # usable and that they have provided suitable FLAGS/LDFLAGS
        JAVA_HOME=/does_not_exist
    fi
    export JAVA_HOME
    AX_JNI_INCLUDE_DIR
    export JAVA_HOME=$saved_JAVA_HOME
    for JNI_DIR in $JNI_INCLUDE_DIRS; do
        SWIG_JAVA_CPPFLAGS="$SWIG_JAVA_CPPFLAGS -I$JNI_DIR"
    done
    SWIG_JAVA_OPT="-java"
    AC_SUBST([SWIG_JAVA_CPPFLAGS])
    AC_SUBST([SWIG_JAVA_OPT])
    AX_CHECK_COMPILE_FLAG([-DSWIG_JAVA_BUILD=1], [AM_CFLAGS="$AM_CFLAGS -DSWIG_JAVA_BUILD=1"])
fi

if test "x$JAVA_HOME" != "x"; then
    JAVA="$JAVA_HOME/bin/java"
    AC_SUBST([JAVA])
    JAVAC="$JAVA_HOME/bin/javac"
    AC_SUBST([JAVAC])
    JAR="$JAVA_HOME/bin/jar"
    AC_SUBST([JAR])
else
    AC_CHECK_PROGS(JAVA, [java])
    AC_CHECK_PROGS(JAVAC, [javac])
    AC_CHECK_PROGS(JAR, [jar])
fi
AM_CONDITIONAL([HAVE_JAVA], [test "x$JAVA" != "x"])
AM_CONDITIONAL([HAVE_JAVAC], [test "x$JAVAC" != "x"])
if test "x$swig_java" == "xyes"; then
    if test "x$JAVAC" != "x"; then
        if test "x$JAVA" != "x"; then
            # Only run tests if we have java-swig, compiler and interpreter
            run_java_tests="yes"
        fi
    fi
fi
AM_CONDITIONAL([RUN_JAVA_TESTS], [test "x$run_java_tests" != "x"])
JAVAC_TARGET=1.7
AC_SUBST([JAVAC_TARGET])

AC_SUBST([AM_CFLAGS])

if test "x$enable_static" == "xyes"; then
    CTEST_EXTRA_STATIC='$(libwallycore_la_LIBADD)'
fi
AC_SUBST([CTEST_EXTRA_STATIC])

AM_CONDITIONAL([SHARED_BUILD_ENABLED], [test "x$enable_shared" == "xyes"])

AC_CONFIG_FILES([
 Makefile
 src/Makefile
 src/wallycore.pc
])

secp_jni="--disable-jni"
if test "x$swig_java" == "xyes"; then
    secp_jni="--enable-jni"
fi

if test x"$cross_compiling" = "xyes"; then
    # For cross compiling we assume the users host O/S Java install is not
    # usable and that they have provided suitable FLAGS/LDFLAGS
    export JAVA_HOME=/does_not_exist
fi
export CC
export CFLAGS
export AR
export ARFLAGS
export AR_FLAGS
export LD
export LDFLAGS
ac_configure_args="${ac_configure_args} --disable-shared ${secp_jni} --with-pic --with-bignum=no --enable-experimental --enable-module-ecdh --enable-module-recovery --enable-module-rangeproof --enable-module-surjectionproof --enable-module-whitelist --enable-module-generator --enable-openssl-tests=no --enable-tests=no --enable-exhaustive-tests=no --enable-benchmark=no --disable-dependency-tracking"
AC_CONFIG_SUBDIRS([src/secp256k1])


AC_OUTPUT
